Jx.declare('Jx.lib.Function', {

	$singleton: true,

	$helpers: {

		'Str': 'Jx.lib.String'

	},

	$actions: [

		'defer',

		'interval',

		'buffer',

		'bind',

		'stop',

		'restart'

	],

	synthesizeOptions: [ 'fn', 'scope', 'args', 'delay', 'buffer', 'autoDestroy' ],

	synthesizeModes: {

		'buffer': 'Buffered',

		'delay': 'Defered',

		'interval': 'Interval',

		'bind': 'Bind'

	},

	defered: undefined,

	monitored: undefined,

	monitoredIdPrefix: 'jx-fn',

	__callback_ClearAllDefered: function(n, v, o){

		this.stop(n);

	},

	__mod_SynthesizeDeferedFunction: function(o, fn, delay, isTimeout){

		fn.delay = delay;

		fn.isTimeout = isTimeout;

		fn.Mgr = this;

		this.__mod_SynthesizeFunction(o, fn);

		o.pid = this.__help_RunDeferedFunction(fn);

	},

	__mod_SynthesizeFunction: function(o, fn){

		var specs = o.specs;

		fn.fn = specs[0];

		fn.scope = specs[1];

		fn.args = specs[2];

	},

	__mod_FormatDeferedParameters: function(o, delay, fn, scope, args){

		var J = Jx,

			isObject = J.isObject(delay),

			delayTimeout = isObject ? delay.delay : delay;

		if (J.isNumber(o.delay = delayTimeout)){

			if (isObject){

				this.__mod_FormatSpecsParameters(o, delay);

			} else {

				this.__mod_FormatSpecsParameters(o, fn, scope, args);

			}

		} else {

			o.valid = false;

		}

	},

	__mod_FormatSpecsParameters: function(o, fn, scope, args){

		var J = Jx;

		if (J.isObject(fn)){

			args = fn.args;

			scope = fn.scope;

			fn = fn.fn;

		}

		if (J.isFunction(fn)){

			scope = typeof scope == 'undefined' ? null : scope;

			if (!J.isArray(args)){

				args = J.isObject(args) && J.isNumber(args.length) ?

					Array.prototype.slice.call(args, 0)

					:

					[];

			}

			o.specs = [ fn, scope, args ];

			o.execAfter = true;

		} else {

			o.valid = false;

		}

	},

	__mod_ClearSpecsParameters: function(o){

		var specs = o.specs;

		if (Jx.isArray(specs)){

			specs.splice(0, specs.length);

		}

		delete o.specs;

	},

	__help_RunDeferedFunction: function(handler){

		if (handler.pid){

			this.__help_StopDeferedFunction(handler);

		}

		//-- start timeout/interval
		this.defered[

			handler.pid = Jx.GLOBAL[

				handler.isTimeout ?

					'setTimeout'

					:

					'setInterval'

			]( handler, handler.delay )

		] = handler;

		return handler.pid;

	},

	__help_StopDeferedFunction: function(handler){

		var id = handler.pid;

		if (id){

			Jx.GLOBAL[

				handler.isTimeout ?

					'clearTimeout'

					:

					'clearInterval'

			]( id );

			delete this.defered[id];

			delete handler.pid;

		}

	},

	constructor: function(){

		this.monitored = {};

		this.$super(arguments);

	},

	onConstruct: function(){

		this.defered = {};

		this.$super(arguments);

	},

	onDestruct: function(){

		Jx.each(this.defered, this.__callback_ClearAllDefered, this);

		this.$super(arguments);

	},

	allowDefer: function(o, delay, fn, scope, args){

		this.__mod_FormatDeferedParameters.apply(this, arguments);

		o.isTimeout = true;

		o.returnValue = 0;

	},

	onDefer: function(o){

		function Defered_Call(){

			var _ = arguments.callee;

			if (_.fn){

				_.fn.apply( _.scope, _.args );

				_.Mgr.stop(_.pid);

			}

		}

		this.__mod_SynthesizeDeferedFunction(o, Defered_Call, o.delay, true);

		o.returnValue = o.pid;

	},

	onAfterDefer: function(o){

		this.__mod_ClearSpecsParameters(o);

	},

	allowInterval: function(o, delay, fn, scope, args){

		this.__mod_FormatDeferedParameters.apply(this, arguments);

		o.isTimeout = false;

		o.returnValue = 0;

	},

	onInterval: function(o){

		function Interval_Call(){

			var _ = arguments.callee;

			if (_.fn){

				_.fn.apply( _.scope, _.args );

			}

		}

		this.__mod_SynthesizeDeferedFunction(o, Interval_Call, o.delay, false);

		o.returnValue = o.pid;

	},

	onAfterInterval: function(o){

		this.__mod_ClearSpecsParameters(o);

	},

	allowBuffer: function(o, interval, fn, scope, args){

		this.__mod_FormatDeferedParameters.apply(this, arguments);

		o.returnValue = void(0);

	},

	onBuffer: function(o){

		function Buffered_Call(){

			var _ = arguments.callee,

				limit = _.limit;

			if (_.fn &&

				(!limit || (++_.execCount) > limit)

			){

				_.execCount = 1;

				_.fn.apply(_.scope,

					_.args.concat(

						Array.prototype.slice.call(arguments, 0)

					)
				);

			}

		}

		this.__mod_SynthesizeFunction(o, Buffered_Call);

		Buffered_Call.execCount = 1;

		Buffered_Call.limit = o.delay;

		Buffered_Call.Mgr = this;

		o.returnValue = Buffered_Call;

	},

	onAfterBuffer: function(o){

		this.__mod_ClearSpecsParameters(o);

	},

	allowBind: function(o, fn, scope, args){

		this.__mod_FormatSpecsParameters(o, fn, scope, args);

		o.returnValue = void(0);

	},

	onBind: function(o){

		function Bind_Call(){

			var _ = arguments.callee;

			return _.fn ?

				_.fn.apply(_.scope,

					_.args.concat(

						Array.prototype.slice.call(arguments, 0)

					)

				)

				:

				void(0);

		}

		this.__mod_SynthesizeFunction(o, Bind_Call);

		o.returnValue = Bind_Call;

	},

	onAfterBind: function(o){

		this.__mod_ClearSpecsParameters(o);

	},

	allowStop: function(o, id){

		o.valid = this.isRunning(id);

	},

	onStop: function(o, id){

		var handler = this.defered[id],

			J = Jx;

		this.__help_StopDeferedFunction(handler);

		delete this.defered[ id ];

		if (J.isArray(handler.args)){

			J.clearObject(handler.args);

		}

		J.clearObject(handler);

	},

	allowRestart: function(o, id){

		o.valid = this.isRunning(id);

		o.returnValue = 0;

	},

	onRestart: function(o, id){

		o.returnValue = this.__help_RunDeferedFunction(o, this.defered[id]);

	},

	isRunning: function(id){

		return this.defered.hasOwnProperty(id);

	}



});
